package com.trade.core.trade;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.trade.core.context.TradeContextHandler;
import com.trade.core.exception.TradeBaseException;
import lombok.extern.slf4j.Slf4j;
import com.trade.core.message.TradeObjectRestResponse;
import com.trade.core.utils.BeanMapUtil;
import com.trade.core.utils.GenericsUtils;
import com.trade.core.utils.GsonUtils;

import java.util.Map;

/**
 * @author sven.wang
 * @version 2021/12/9 20:09
 * @Description: 交易步骤处理抽象类
 * 注:
 * 泛型P:步骤输入对象
 * 泛型R:步骤输出对象
 * 泛型P、R为步骤入参对象(只能为包装类型,不可为基础类型或基础类型的包装类型,如:String、Integer、char、int等),不符合规范步骤执行会抛异常
 */
@Slf4j
public abstract class TradeProcessorStepAbstract<P,R> implements ITradeProcessorStep {

    /**
     * 上下文步骤入参检查
     *
     * @param param 上下文步骤入参
     */
    public abstract void checkParam(P param);

    /**
     * 实际执行步骤
     * <p>
     * 注:返回对象(只能为包装类型,不可为基础类型或基础类型的包装类型,如:String、Integer、char、int等),不符合规范步骤执行会抛异常
     *
     * @param param 步骤入参
     * @return 步骤执行响应结果
     */
    public abstract TradeObjectRestResponse doProcess(P param);

    /**
     * 上下文步骤出参检查
     *
     * @param outParam 上下文步骤出参
     */
    public abstract void checkOutParam(R outParam);

    /**
     * 执行步骤
     *
     * @return 步骤响应结果
     */
    @Override
    public TradeObjectRestResponse<R> process() {
        //1.获取上下文
        P param = getContext();
        if(param != null){
            isValidJsonObject(param);
        }
        //2.检查上下文
        checkParam(param);
        //3.实际执行步骤
        TradeObjectRestResponse<R> response = doProcess(param);
        if (response.getData() != null) {
            isValidJsonObject(response.getData());
        }
        checkOutParam(response.getData());
        if (response.getData() != null) {
            Map<String, Object> stringMap = (Map<String, Object>) BeanMapUtil.beanToMap(response.getData());
            //如果返回的上下文字段有null的剔除不进行放入上下文操作
            stringMap.entrySet().removeIf(entry -> entry.getValue() == null);
            //将返回内容设置入上下文
            TradeContextHandler.putAll(stringMap);
        }
        return response;
    }

    /**
     * 获取上下文步骤入参
     *
     * @return 上下文步骤入参
     */
    public P getContext() {
        try {
            //获取入参泛型
            Class<P> dc = GenericsUtils.getSuperClassGenricType(this.getClass(), 0);
            Map<String, Object> map = TradeContextHandler.getMap();
            P p = dc.newInstance();
            if(!map.isEmpty() && p.getClass() != Object.class){
                p = (P) BeanMapUtil.mapToBean(map, p.getClass());
            }
            if(log.isDebugEnabled()){
                log.debug("trade context :{}", GsonUtils.toJson(p));
            }
            return p;
        } catch (Exception e) {
            log.error("trade response context change failed, step is : {} ,exception:", this.step(), e);
            throw new TradeBaseException("trade response context change failed");
        }
    }



    /**
     * 检查对象是否是有效的JsonObject
     *
     * @param o 待检查对象
     */
    private void isValidJsonObject(Object o) {
        String str = GsonUtils.toJson(o);
        JsonParser jsonParser = new JsonParser();
        try {
            JsonElement jsonElement = jsonParser.parse(str);
            if (!jsonElement.isJsonObject()) {
                log.error("trade param or response is not valid JsonObject, step is : {}, string : {} ", this.step(), str);
                throw new TradeBaseException("trade param or response is not valid JsonObject, step is : " + this.step());
            }
        } catch (Exception e) {
            log.error("trade param or response is not valid JsonObject, step is : {}, string : {}, exception : ", this.step(), str, e);
            throw new TradeBaseException("trade param or response is not valid JsonObject, step is : " + this.step());
        }

    }
}
